﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Communication.Lib.Core;
using log4net;

namespace Communication.Lib.Communication
{
    /// <summary>
    /// 用于监听ListenPort，将获取到的信息加工并转发给ConnectPort。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class TCPCommunicate<T> : ICommunicatable<T> where T : BaseInfo
    {
        private ILog _logger = LogManager.GetLogger(typeof(TCPCommunicate<T>));
        private Socket client, server;
        private IProtocolable<T> protocolable;
        private DateTime _lastAliveTime;
        private bool isServer;
        private Thread recvThread;

        #region 属性

        public string HostName { get; }

        public int ConnectPortPort { get; }

        public int ListenPort { get; }

        #endregion 属性

        /// <summary>
        /// client 处理消息事件
        /// </summary>
        public event Action<T> RecvHandler;

        /// <summary>
        /// server 处理消息事件
        /// </summary>
        public event Action<Socket, T> RecvCallBack;

        public bool IsAlive => (DateTime.Now - _lastAliveTime).TotalSeconds <= 30;

        public TCPCommunicate(string hostname, int connectPort, int listen, IProtocolable<T> protocol, bool isServer = false)
        {
            protocolable = protocol;
            HostName = hostname;
            ConnectPortPort = connectPort;
            ListenPort = listen;
            this.isServer = isServer;
            if (isServer)
            {
                server = Socket.OSSupportsIPv4
                    ? new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
                    : new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);

                server.Bind(new IPEndPoint(IPAddress.Parse(hostname), ListenPort));
                server.Listen(10);
            }
            else
            {
                client = Socket.OSSupportsIPv4 ? new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) : new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
            }

            recvThread = new Thread(RecvThread);
            recvThread.IsBackground = true;
        }

        private void RecvThread()
        {
            if (isServer)
            {
                while (true)
                {
                    try
                    {
                        var accept = server.Accept();
                        string skip = ((IPEndPoint)accept.RemoteEndPoint).Address.ToString();
                        Thread skHandler = new Thread(RecvHandle);
                        skHandler.Name = skip;
                        skHandler.IsBackground = true;
                        skHandler.Start(accept);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"[{GetType().Name}]  RecvSocketError. .ex:{ex}");
                    }
                }
            }

            {
                Thread skHandler = new Thread(RecvHandle);
                skHandler.IsBackground = true;
                skHandler.Start(client);
            }
        }

        private void RecvHandle(object obj)
        {
            var accept = obj as Socket;
            if (accept is null) return;
            while (true)
            {
                try
                {
                    var buffer = new byte[2048];
                    if (!accept.Connected)
                    {
                        break;
                    }
                    var length = accept.Receive(buffer);
                    List<byte> data = new List<byte>();
                    for (int i = 0; i < length; i++)
                    {
                        data.Add(buffer[i]);
                    }
                    protocolable.AddBytes(data.ToArray());
                    var t = protocolable.DecodeData();
                    while (t != null)
                    {
                        _lastAliveTime = DateTime.Now;
                        RecvHandler?.Invoke(t);
                        RecvCallBack?.Invoke(accept, t);
                        t = protocolable.DecodeData();
                    }
                }
                catch (Exception e)
                {
                    _logger.Error($"[{GetType().Name}] RecvHandleError. ex:{e}");
                }
            }
        }

        public bool SendData(T info)
        {
            try
            {
                var data = protocolable.EncodeBytes(info);

                return SendData(data);
            }
            catch (Exception e)
            {
                _logger.Error($"[{GetType().Name}]  SendDataError. data:{info.ToJson()}.ex:{e}");
            }

            return false;
        }

        public bool SendData(byte[] bytes)
        {
            if (bytes == null || bytes.Length <= 0 || !client.Connected)
            {
                return false;
            }
            try
            {
                var i = client?.Send(bytes, 0, bytes.Length, SocketFlags.None);
                _lastAliveTime = DateTime.Now;
                return i > 0;
            }
            catch (Exception e)
            {
                _logger.Error($"[{GetType().Name}]  SendDataError. data:{bytes.FormatAsHex()}.ex:{e}");
            }
            return false;
        }

        public void Start()
        {
            if (!isServer)
            {
                if (!client.Connected)
                {
                    client.Connect(HostName, ConnectPortPort);
                }
            }
            recvThread.Start();
        }

        public void Stop()
        {
            if (!isServer)
            {
                if (client.Connected)
                {
                    client.Close();
                }
            }
            recvThread.Abort();
            recvThread = new Thread(RecvThread);
            recvThread.IsBackground = true;
        }
    }
}